﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace HF
{
    /// <summary>
    /// 基于 <see cref="Expression"/> 简单封装的高效读写对象属性或字段的类
    /// </summary>
    public static partial class HExpr
    {
        public delegate void ActionRef<T1>(ref T1 arg1);
        public delegate void ActionRef<T1, in T2>(ref T1 arg1, T2 arg2);
        public delegate void ActionRef<T1, in T2, in T3>(ref T1 arg1, T2 arg2, T3 arg3);
        public delegate void ActionRef<T1, in T2, in T3, in T4>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
        public delegate void ActionRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);

        public delegate TResult FuncRef<T1, out TResult>(ref T1 arg1);
        public delegate TResult FuncRef<T1, in T2, out TResult>(ref T1 arg1, T2 arg2);
        public delegate TResult FuncRef<T1, in T2, in T3, out TResult>(ref T1 arg1, T2 arg2, T3 arg3);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
        public delegate TResult FuncRef<T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, out TResult>(ref T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);

        private static readonly Type[] _RefActionTypes =
        {
            //typeof(ActionRef<>),
            typeof(ActionRef<,>),
            typeof(ActionRef<,,>),
            typeof(ActionRef<,,,>),
            typeof(ActionRef<,,,,>),
            typeof(ActionRef<,,,,,>),
            typeof(ActionRef<,,,,,,>),
            typeof(ActionRef<,,,,,,,>),
            typeof(ActionRef<,,,,,,,,>),
            typeof(ActionRef<,,,,,,,,,>),
            typeof(ActionRef<,,,,,,,,,,>),
            typeof(ActionRef<,,,,,,,,,,,>),
            typeof(ActionRef<,,,,,,,,,,,,>),
            typeof(ActionRef<,,,,,,,,,,,,,>),
            typeof(ActionRef<,,,,,,,,,,,,,,>),
            typeof(ActionRef<,,,,,,,,,,,,,,,>),
        };

        private static readonly Type[] _ActionTypes =
        {
            //typeof(Action<>),
            typeof(Action<,>),
            typeof(Action<,,>),
            typeof(Action<,,,>),
            typeof(Action<,,,,>),
            typeof(Action<,,,,,>),
            typeof(Action<,,,,,,>),
            typeof(Action<,,,,,,,>),
            typeof(Action<,,,,,,,,>),
            typeof(Action<,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,,,,>),
            typeof(Action<,,,,,,,,,,,,,,,>),
        };

        private static readonly Type[] _RefFunTypes =
        {
            typeof(FuncRef<,>),
            typeof(FuncRef<,,>),
            typeof(FuncRef<,,,>),
            typeof(FuncRef<,,,,>),
            typeof(FuncRef<,,,,,>),
            typeof(FuncRef<,,,,,,>),
            typeof(FuncRef<,,,,,,,>),
            typeof(FuncRef<,,,,,,,,>),
            typeof(FuncRef<,,,,,,,,,>),
            typeof(FuncRef<,,,,,,,,,,>),
            typeof(FuncRef<,,,,,,,,,,,>),
            typeof(FuncRef<,,,,,,,,,,,,>),
            typeof(FuncRef<,,,,,,,,,,,,,>),
            typeof(FuncRef<,,,,,,,,,,,,,,>),
            typeof(FuncRef<,,,,,,,,,,,,,,,>),
            typeof(FuncRef<,,,,,,,,,,,,,,,,>),
        };

        private static readonly Type[] _FunTypes =
        {
            typeof(Func<,>),
            typeof(Func<,,>),
            typeof(Func<,,,>),
            typeof(Func<,,,,>),
            typeof(Func<,,,,,>),
            typeof(Func<,,,,,,>),
            typeof(Func<,,,,,,,>),
            typeof(Func<,,,,,,,,>),
            typeof(Func<,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,,,,>),
            typeof(Func<,,,,,,,,,,,,,,,,>),
        };

        /// <summary>
        /// 创建指定参数类型 <paramref name="types"/> 的 <see cref="Delegate"/>
        /// </summary>
        /// <param name="types">参数类型集合</param>
        /// <param name="isAction">是 <see cref="Action"/> 还是 <see cref="Func{TResult}"/></param>
        /// <param name="isRefby">是否采用 <see langword="ref"/> 引用类型</param>
        /// <returns>返回创建好的 <see cref="Delegate"/></returns>
        private static Type GetDelegate(Type[] types, bool isAction, bool isRefby)
        {
            var len = types.Length;
            if (len < 2 || len > 16)
            {
                new IndexOutOfRangeException($"{nameof(types)} 参数超过上限").Throw();
            }

            Type t = isAction ?
                isRefby ? _RefActionTypes[len - 2] : _ActionTypes[len - 2] :
                isRefby ? _RefFunTypes[len - 2] : _FunTypes[len - 2];

            var result = t.MakeGenericType(types);
            return result;
        }

        /// <summary>
        /// <see cref="BindingFlags.Public"/> <see langword="|"/> <see cref="BindingFlags.NonPublic"/>
        /// </summary>
        private const BindingFlags C_DEF_BF = BindingFlags.Public | BindingFlags.NonPublic;

        /// <summary>
        /// 生成 <paramref name="param"/> 到指定类型 <paramref name="type"/> 的 <see cref="Expression"/>
        /// </summary>
        /// <param name="param">待转换的 Expression</param>
        /// <param name="type">转换的类型</param>
        /// <returns></returns>
        private static Expression ConvertExpr(Expression param, Type type)
        {
            var code = Type.GetTypeCode(type);
            if (type.IsEnum)
            {
                var method = typeof(Property).GetMethod(nameof(ConvertEnum), BindingFlags.Static | BindingFlags.NonPublic);
                return Expression.Convert(Expression.Call(method, param, Expression.Constant(type)), type);
            }
            else if (code == TypeCode.DateTime)
            {
                var method = typeof(Property).GetMethod(nameof(ConvertDataTime), BindingFlags.Static | BindingFlags.NonPublic);
                return Expression.Call(method, param);
            }
            else if (code == TypeCode.String)
            {
                return Expression.Call(param, nameof(ToString), Array.Empty<Type>());
            }
            else if (code >= TypeCode.Boolean && code <= TypeCode.DateTime)
            {
                var memth = typeof(Convert).GetMethod($"To{code}", new Type[] { typeof(object) });
                return Expression.Call(memth, param);
            }
            else
            {
                return Expression.Convert(param, type);
            }
        }

        /// <summary>转换对象到枚举</summary>
        /// <param name="obj">对象</param>
        /// <param name="resultType">枚举类型</param>
        /// <returns></returns>
        private static object ConvertEnum(object obj, Type resultType)
        {
            object result = obj is string str ?             //字符串
                int.TryParse(str, out var num) ?
                    Enum.ToObject(resultType, num) :        //整数型字符串
                    Enum.Parse(resultType, str, true) :     //字符串
                Enum.ToObject(resultType, (int)obj);        //强制整形转换
            return result;
        }

        /// <summary>转换对象到枚举</summary>
        /// <param name="obj">对象</param>
        /// <param name="resultType">枚举类型</param>
        /// <returns></returns>
        private static DateTime ConvertDataTime(object obj)
        {
            if (obj is DateTime resutl)
            {
                return resutl;
            }

            if (obj is string str && DateTime.TryParse(str, out resutl))
            {
                return resutl;
            }

            resutl = Convert.ToDateTime(obj);
            return resutl;
        }

        /// <summary>
        /// 获取指定类型的全名称
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static string GetTypeFullName(this Type type)
        {
            return type.IsGenericType ?
                type.FullName.Replace("+", ".").Split("`", 2)[0] + "<"
                    + type.GenericTypeArguments.Select(v => v.Name)
                            .Aggregate((u, d) => $"{u}, {d}") + ">" :
                type.FullName.Replace("+", ".").Split("`", 2)[0];
        }

        /// <summary>
        /// 获取指定 <paramref name="source"/> 的属性或字段(包括静态和私有)。
        /// </summary>
        /// <param name="source">待获取属性或字段的类型</param>
        /// <param name="memName">属性或字段名称</param>
        /// <param name="isWrite">属性或字段是否可写</param>
        /// <param name="isStatic">返回属性或字段是否静态</param>
        /// <param name="returnType">返回读取属性或字段的类型</param>
        private static MemberInfo GetPropertyField(Type source, string memName, bool isWrite, out bool isStatic, out Type returnType)
        {
            const BindingFlags C_BindingFlags = C_DEF_BF | BindingFlags.Static | BindingFlags.Instance;
            source.CheckArgumentNull(nameof(source));
            memName = memName.CheckArgumentNull(nameof(memName)).Trim();
            var fieldInfo = source.GetField(memName, C_BindingFlags);
            if (fieldInfo != null)
            {
                isStatic = fieldInfo.IsStatic;
                returnType = fieldInfo.FieldType;
                return fieldInfo;
            }

            var pro = source.GetProperty(memName, C_BindingFlags);
            if (pro == null)
            {
                throw new Exception($"未从类型 : {source.FullName} 中查询到名为 {memName} 的属性或成员.");
            }

            if (!isWrite && !pro.CanRead)
            {
                throw new Exception($"{source.FullName} 中的 {memName} 为不可读的属性.");
            }

            if (isWrite && !pro.CanWrite)
            {
                throw new Exception($"{source.FullName} 中的 {memName} 为不可写的属性.");
            }

            isStatic = pro.GetMethod.IsStatic;
            returnType = pro.PropertyType;
            return pro;
        }

        /// <summary>
        /// 获取指定索引属性信息
        /// </summary>
        /// <param name="source">属性类型</param>
        /// <param name="paramTypes">索引参数类型</param>
        /// <param name="isWrite">是否可写</param>
        /// <returns>返回查询到的属性索引信息</returns>
        private static PropertyInfo GetIndexProperty(Type source, Type[] paramTypes, bool isWrite)
        {
            const BindingFlags C_BindingFlags = C_DEF_BF | BindingFlags.Instance | BindingFlags.GetProperty;
            source.CheckArgumentNull(nameof(source));
            var pros = source.GetProperties(C_BindingFlags);
            PropertyInfo result = default;
            foreach (var pro in pros)
            {
                var proTypes = pro.GetIndexParameters();
                if (proTypes.Length != paramTypes.Length)
                {
                    continue;
                }

                var b = false;
                for (int i = 0; i < proTypes.Length; i++)
                {
                    if (paramTypes[i].Equals(proTypes[i]))
                    {
                        b = true;
                        break;
                    }
                }

                if (b)
                {
                    continue;
                }

                result = pro;
                break;
            }

            if (result == null)
            {
                var s = paramTypes.Select(v => v.Name).Aggregate((u, d) => $"{u}, {d}");
                throw new Exception($"未查询到 : {source.FullName}.[{s}] 的索引属性。");
            }


            if (!isWrite && !result.CanRead)
            {
                var s = paramTypes.Select(v => v.Name).Aggregate((u, d) => $"{u}, {d}");
                throw new Exception($"类型 : {source.FullName}.[{s}] 为不可读索引属性。");
            }

            if (isWrite && !result.CanWrite)
            {
                var s = paramTypes.Select(v => v.Name).Aggregate((u, d) => $"{u}, {d}");
                throw new Exception($"类型 : {source.FullName}.[{s}] 为不可写索引属性。");
            }

            return result;
        }
    }
}
